using Basket.Api.Data;
using Basket.Api.Domain;
using Basket.Api.Features.Baskets.GetBasket;
using BuildingBlocks.Application.Authentication;
using BuildingBlocks.Application.Messaging;
using BuildingBlocks.Domain;
using Microsoft.EntityFrameworkCore;

namespace Basket.Api.Features.Baskets.Upsert;

public sealed record UpsertBasketCommand(
    IReadOnlyCollection<UpsertBasketItemRequest> Items) : ICommand<BasketResponse>;

internal sealed class UpsertBasketCommandHandler(BasketDbContext dbContext, ICurrentUser currentUser)
    : ICommandHandler<UpsertBasketCommand, BasketResponse>
{
    public async ValueTask<Result<BasketResponse>> Handle(
        UpsertBasketCommand request,
        CancellationToken ct)
    {
        Guid userId = currentUser.UserId;
        DateTime now = DateTime.UtcNow;

        List<BasketItem> mappedItems = request.Items
            .Select(item => new BasketItem
            {
                ProductId = item.ProductId,
                ProductName = item.ProductName,
                UnitPrice = item.UnitPrice,
                Quantity = item.Quantity
            })
            .ToList();

        var payload = new ShoppingBasket
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            Items = mappedItems,
            CreatedAt = now,
            UpdatedAt = now
        };

        await dbContext.Baskets
            .Upsert(payload)
            .On(b => b.UserId)
            .WhenMatched((existing, inserted) => new ShoppingBasket
            {
                Id = existing.Id,
                UserId = existing.UserId,
                CreatedAt = existing.CreatedAt,
                UpdatedAt = inserted.UpdatedAt,
                Items = inserted.Items
            })
            .RunAsync(ct);

        var persisted = await dbContext.Baskets
            .AsNoTracking()
            .Include(b => b.Items)
            .FirstAsync(b => b.UserId == userId, ct);

        List<BasketItemResponse> finalItems = persisted.Items
            .Select(item => new BasketItemResponse(item.ProductId, item.ProductName, item.UnitPrice, item.Quantity))
            .ToList();

        decimal total = finalItems.Sum(i => i.UnitPrice * i.Quantity);

        return Result.Success(new BasketResponse(
            persisted.UserId,
            finalItems,
            total,
            persisted.UpdatedAt));
    }
}
